#pragma once

#include "Types.h"
#include <xml/stream.h>
#include <typelib/xml.h>
#include <read_enum.h>
#include <MyControls/EnumProperty.h>

namespace My {

enum BezierNodeType {
	bnFlat,
	bnSymmetrical,
	bnSmooth,
	bnCusp
};

const char * const BezierNodeTypeStrings[] = 
	{ "flat", "symmetrical", "smooth", "cusp", 0 };

DEFINE_INLINE_ENUM_IO_OPERATORS(BezierNodeType, BezierNodeTypeStrings)

SPECIALIZE_ENUM_TYPE_CONTROL(BezierNodeType)
template<>
EnumItem<BezierNodeType> TypedControl<BezierNodeType>::Items[] = 
{
	"Flat", bnFlat,
	"Symmetrical", bnSymmetrical,
	"Smooth", bnSmooth,
	"Cusp", bnCusp
};

struct BezierNode {
	BezierNodeType	type;
	point<float>	position;
	point<float>	control[2];
public:
	BezierNode();
	bool operator == (const BezierNode &) const;
	bool operator != (const BezierNode &) const;
};

inline xml::stream & operator || (xml::stream & s, BezierNode & n) {
	return s
		|| xml::attr("type", n.type) 
		|| xml::child("p", n.position) 
		|| xml::child("c0", n.control[0]) 
		|| xml::child("c1", n.control[1]);
}

point<float> DLL_API BezierAt(
	const point<float> & p0, 
	const point<float> & p1, 
	const point<float> & p2, 
	const point<float> & p3, 
	float t);

bool DLL_API BezierNearest(
	const point<float> & p0, 
	const point<float> & p1, 
	const point<float> & p2, 
	const point<float> & p3, 
	const point<float> & q,
	float accuracy,
	float threshold,
	float & d,
	float & t);

void DLL_API SplitBezier(
	const point<float> & p0,
	point<float> & p1,
	point<float> & p2,
	const point<float> & p3,
	float t, 
	point<float> & c0,
	point<float> & p,
	point<float> & c1);

void DLL_API SetNodeControlPoint(BezierNode & n, Size c, point<float> p);
void DLL_API SetNodeType(BezierNode & n, BezierNodeType);

class Bezier {
public:
	virtual ByteColor GetColor() const = 0;
	virtual bool IsVisible() const = 0;
	virtual bool GetClipping() const = 0;
	virtual Size GetNodeCount() const = 0;
	virtual bool IsClosed() const = 0;
	virtual BezierNode GetNode(Size index) const = 0;
	virtual void Prepend(const BezierNode &) = 0;
	virtual Size Append(const BezierNode &) = 0;
	virtual Size Insert(float t) = 0;
	virtual void Erase(Size) = 0;
	virtual void Close() = 0;
	virtual void SetNode(Size index, const BezierNode &) = 0;
	virtual bool IsSelected() const = 0;
	virtual bool IsPartiallySelected() const = 0;
	virtual bool IsSelected(Size) const = 0;
	virtual void Select() = 0; 
	virtual void Select(Size) = 0; 
	virtual void Deselect() = 0; 
	virtual void Deselect(Size) = 0; 
};

inline Size Prev(const Bezier & b, Size index) 
{ 
	if (!b.IsClosed() && index == 0)
		return index;
	return (b.GetNodeCount() + index - 1) % b.GetNodeCount(); 
}

inline Size Next(const Bezier & b, Size index) 
{ 
	if (!b.IsClosed() && index == b.GetNodeCount() - 1)
		return index;
	return (b.GetNodeCount() + index + 1) % b.GetNodeCount(); 
}

class BezierOwner {
public:
	virtual Size GetBezierCount() const = 0;
	virtual Bezier & GetBezier(Size) const = 0;
	virtual Size CreateBezier(const BezierNode &) = 0;
	virtual void DeleteBezier(Size) = 0;
	virtual void SelectAll() = 0;
	virtual void DeselectAll() = 0;
};

} // My
